#include <usr/libsodium.h>
#include <kernel/mem/mem_part_firstfit.h>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////// Algoritmo de Administración de Memoria de asignación contigua de particiones                ///////////////////
//////////////// Funciones de procesos de Usuario											                 ///////////////////
//////////////// Estrategia Malloc: First Fit 																 ///////////////////
//////////////// Sergio Martin 2009                                                                          ///////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int iHeapUsuarioInicializado = 0;

/**
 * @brief Inicializa el Heap de usuario utilizando la configuración obtenida por el syscall headerinfo 
 * @date 20/11/2009
 */
int iFnInicializarHeapUsuario(){
  int i;

  iFnInicializar(); 
  
  iHeapUsuarioInicializado = 1;
  return 0;
}

/**
 * @brief Utiliza malloc para reservar un en el heap de usuario
 * @date 20/11/2009
 */
void* malloc(long int uiTamanioDeseado){
     long int liPuntero;
   
     if(iHeapUsuarioInicializado == 0)      return -3;

     if(iModo == FIJO && iModoFijo == FIJO_EQUITATIVO)  {
       if(uiTamanioDeseado > liTamFija + sizeof(t_Nodo)) return -2;
       liHeapEnd = (long int)ptrHeap + ((int)(liTamHeap)/liTamFija)*liTamFija;
       }
     if(iModo == FIJO && iModoFijo == FIJO_PROPORCIONAL) liHeapEnd = (long int)ptrHeap + liHeapEndPorcentajes;
     if(iModo == VARIABLE)   liHeapEnd = (long int)ptrHeap + liTamHeap;

     
     liPuntero = (long int)pvFnMalloc(uiTamanioDeseado);
     
     /* Si se quedó sin espacio de heap, intentaremos solicitarle mas espacio al kernel.
        En el caso de que el error en particiones fijas haya sido que el espacio solicitado
        es mayor al tamaño de la particion, esto no se hace por error code. */
     if(liPuntero == -1)  
        {
          if(iModo == VARIABLE)
            {
		  /* Redimensiono el proceso agregandole al heap el nuevo tamaño 
		     Modo rustico. No hay pre-chequeos por espacio contiguo al final, se agrega la cantidad
		     pedida por el malloc, y recién ahí con free hacemos el post-chequeo del espacio libre
		     contiguo y unimos si es que se puede */
		  if(sbrk(uiTamanioDeseado + sizeof(t_Nodo))  == -1) return -1;

		  // Voy hasta el ultimo puntero, y agrego un nuevo nodo inmediatamente despues
		      ptrActual = (t_Nodo*)ptrHeap;
		      while((long int)ptrActual < liHeapEnd) ptrActual = (t_Nodo*)((long int)ptrActual + (long int)ptrActual->liTam);
		      ptrActual->iUsado = 1; // He aqui el truquillo. Le paso un free ahora que se encargue.
		      ptrActual->liTam = uiTamanioDeseado + sizeof(t_Nodo);
	
		  // Defino el nuevo tamaño del heap
		      liTamHeap += uiTamanioDeseado + sizeof(t_Nodo);
		      liHeapEnd = (long int)ptrHeap + liTamHeap;

		  // El free se va a encargar de unir espacios contiguos.
		     iFnFree((long int)ptrActual + sizeof(t_Nodo));

		  // Finalmente intento el malloc de nuevo que ahora si va a funcionar.
		      liPuntero = (long int)pvFnMalloc(uiTamanioDeseado);
           }
         
	 /*if(iModo == FIJO )
            {
		  // Para modo fijo, agrego un nuevo nodo al final 
		  if(sbrk(liTamFija)  == -1) return -1;

		  // Voy hasta el ultimo puntero, y agrego un nuevo nodo inmediatamente despues
		      ptrActual = (t_Nodo*)ptrHeap;
		      while((long int)ptrActual < liHeapEnd) ptrActual = (t_Nodo*)((long int)ptrActual + (long int)ptrActual->liTam);
		      ptrActual->iUsado = 0; // Libre
		      ptrActual->liTam = liTamFija;
	
		  // Defino el nuevo tamaño del heap
		      liTamHeap += liTamFija;
                      liHeapEnd = (long int)ptrHeap + ((int)(liTamHeap)/liTamFija)*liTamFija;

		  // Finalmente intento el malloc de nuevo que ahora si va a funcionar.
		      liPuntero = (long int)pvFnMalloc(uiTamanioDeseado);
           }*/
        }
     return (void*)liPuntero;
}

void* realloc(long int liPunteroNodo, unsigned long ulTamanioNuevo){
     long int liPuntero;

     if(iHeapUsuarioInicializado == 0) return -3;

     if(iModo == FIJO && iModoFijo == FIJO_EQUITATIVO)  {
       if(ulTamanioNuevo > liTamFija + sizeof(t_Nodo)) return -2;
       liHeapEnd = (long int)ptrHeap + ((int)(liTamHeap)/liTamFija)*liTamFija;
       }
     if(iModo == FIJO && iModoFijo == FIJO_PROPORCIONAL) liHeapEnd = (long int)ptrHeap + liHeapEndPorcentajes;
     if(iModo == VARIABLE)   liHeapEnd = (long int)ptrHeap + liTamHeap;

     liPuntero = (long int)pvFnRealloc(liPunteroNodo, ulTamanioNuevo);

    if(liPuntero == -2) // Fallo malloc.  
        {
          if(iModo == VARIABLE)
            {
		  /* Redimensiono el proceso agregandole al heap el nuevo tamaño 
		     Modo rustico. No hay pre-chequeos por espacio contiguo al final, se agrega la cantidad
		     pedida por el malloc, y recién ahí con free hacemos el post-chequeo del espacio libre
		     contiguo y unimos si es que se puede */
		  if(sbrk(ulTamanioNuevo + sizeof(t_Nodo))  == -1) return -1;

		  // Voy hasta el ultimo puntero, y agrego un nuevo nodo inmediatamente despues
		      ptrActual = (t_Nodo*)ptrHeap;
		      while((long int)ptrActual < liHeapEnd) ptrActual = (t_Nodo*)((long int)ptrActual + (long int)ptrActual->liTam);
		      ptrActual->iUsado = 1; // He aqui el truquillo. Le paso un free ahora que se encargue.
		      ptrActual->liTam = ulTamanioNuevo + sizeof(t_Nodo);
	
		  // Defino el nuevo tamaño del heap
		      liTamHeap += ulTamanioNuevo + sizeof(t_Nodo);
		      liHeapEnd = (long int)ptrHeap + liTamHeap;

		  // El free se va a encargar de unir espacios contiguos.
		     iFnFree((long int)ptrActual + sizeof(t_Nodo));

		  // Finalmente intento el malloc de nuevo que ahora si va a funcionar.
		      liPuntero = (long int)pvFnRealloc(liPunteroNodo, ulTamanioNuevo);
           }

	/* if(iModo == FIJO)
            {
		  // Para modo fijo, agrego un nuevo nodo al final 
		  if(sbrk(liTamFija)  == -1) return -1;

		  // Voy hasta el ultimo puntero, y agrego un nuevo nodo inmediatamente despues
		      ptrActual = (t_Nodo*)ptrHeap;
		      while((long int)ptrActual < liHeapEnd) ptrActual = (t_Nodo*)((long int)ptrActual + (long int)ptrActual->liTam);
		      ptrActual->iUsado = 0; // Libre
		      ptrActual->liTam = liTamFija;
	
		  // Defino el nuevo tamaño del heap
		      liTamHeap += liTamFija;
                      liHeapEnd = (long int)ptrHeap + ((int)(liTamHeap)/liTamFija)*liTamFija;

		  // Finalmente intento el malloc de nuevo que ahora si va a funcionar.
		      liPuntero = (long int)pvFnRealloc(liPunteroNodo, ulTamanioNuevo);
           }*/
        }
     return (void*)liPuntero;

}

/**
 * @brief Utiliza free para liberar un segmento dentro del heap de usuario
 * @date 20/11/2009
 */
int free(long int pvParticionALiberar) {
     if(iHeapUsuarioInicializado == 0) return -3;
     return iFnFree((long int)pvParticionALiberar) == -1;
}

/**
 * @brief Utiliza malloc para reservar un en el heap de usuario y pone en 0 todas las posiciones de la particion
 * @date 20/11/2009
 */
void* calloc(long int uiTamanioDeseado){
     long int liPuntero;
   
     if(iHeapUsuarioInicializado == 0)      return -3;

	 liPuntero = pvFnCalloc(uiTamanioDeseado);
	 
     return (void*)liPuntero;
}

/**
 * @brief Lista en pantalla las particiones libres y usadas y su tamaño.
 * @date 20/11/2009
 */
void vFnListar()
  {
     long int liTamAnt;
     t_Nodo* ptrSig;
     int i;
     
     if(iHeapUsuarioInicializado == 0)      return -3;

     if(iModo == FIJO && iModoFijo == FIJO_EQUITATIVO)     liHeapEnd = (long int)ptrHeap + ((int)(liTamHeap)/liTamFija)*liTamFija;

     if(iModo == FIJO && iModoFijo == FIJO_PROPORCIONAL)    liHeapEnd =  (long int)ptrHeap + liHeapEndPorcentajes;

     if(iModo == VARIABLE)   liHeapEnd = (long int)ptrHeap + liTamHeap;
     
     ptrActual = (t_Nodo*)ptrHeap;
     

     i = 1;

     iFnImprimir("\nListado de particiones en heap de proceso usuario.");
     iFnImprimir("\n--------------------------------------------------");
     iFnImprimir("\nNumero de Particion             Tamanio (KB)              Libre/Usado");

     while((long int)ptrActual < liHeapEnd)
      {
       if((long int)ptrActual->liTam <= 0) break;
       iFnImprimir("\n             ");systest(i);iFnImprimir("                  ");systest((long int)ptrActual->liTam/1024);
       if(ptrActual->iUsado == LIBRE) iFnImprimir("                    Libre");
       else iFnImprimir("                    Usado");
       ptrActual = (t_Nodo*)((long int)ptrActual + (long int)ptrActual->liTam);
       i++;
      }
  }


